<?php
include_once( 'CPayRequest.php' );

class CPayment
{
	//const URL = 'https://3ds.payment.ru/cgi-bin/cgi_link';
	const URL                 = 'http://193.200.10.117:8080/cgi-bin/cgi_link';
	const TRTYPE_PAY          = 1;
	const TRTYPE_CANCEL       = 22;
	const TRTYPE_PREAUTH      = 0;
	const TRTYPE_FINISH       = 21;
	const RESULT_SUCCESS      = 0;
	const RESULT_REPEAT       = 1;
	const RESULT_BANK_CANCEL  = 2;
	const RESULT_PAYGW_CANCEL = 3;
	const KEY                 = 'C50E41160302E0F5D6D59F1AA3925C45';
	const MERCH_NAME          = 'test';
	const MERCHANT            = '790367686219999';
	const BACKREF             = 'http://raybt.ru/';
	const TERMINAL            = '79036861';
	const EMAIL               = 'almatov.us@gmail.com';

	private static $FIELD_NAMES = [ 'AMOUNT', 'CURRENCY', 'ORDER', 'DESC', 'TERMINAL', 'TRTYPE', 'MERCH_NAME', 'MERCHANT', 'EMAIL',
		'TIMESTAMP', 'NONCE', 'BACKREF', 'P_SIGN', 'RC', 'RCTEXT', 'AUTHCODE', 'RRN', 'INT_REF', 'RESULT', 'ORG_AMOUNT', 'NAME', 'CARD', 'CHANNEL' ];

	public static function pay( $amount, $desc, $backref = self::BACKREF )
	{
		$currency   = 'RUB';
		$order      = self::getTimestamp();
		$timestamp  = self::getTimestamp();
		$nonce      = self::getNonce();
		$terminal   = self::TERMINAL;
		$trtype     = self::TRTYPE_PAY;
		$merch_name = self::MERCH_NAME;
		$merchant   = self::MERCHANT;
		$email      = self::EMAIL;
		$hmac       = self::getHMAC( $amount, $currency, $order, $merch_name, $merchant, $terminal, $email, $trtype, $timestamp, $nonce, $backref );
		$p_sign     = self::getPSign( $hmac );

		$request = [
			'AMOUNT'     => $amount, 'CURRENCY' => $currency, 'ORDER' => $order,
			'DESC'       => $desc, 'TERMINAL' => $terminal, 'TRTYPE' => $trtype,
			'MERCH_NAME' => $merch_name, 'MERCHANT' => $merchant, 'EMAIL' => $email,
			'TIMESTAMP'  => $timestamp, 'NONCE' => $nonce, 'BACKREF' => $backref, 'P_SIGN' => $p_sign,
		];

		self::saveRequest( $request );
		self::showForm( $request );
	}

	public static function preauth( $amount, $desc, $backref = self::BACKREF )
	{
		$currency   = 'RUB';
		$order      = self::getTimestamp();
		$timestamp  = self::getTimestamp();
		$nonce      = self::getNonce();
		$terminal   = self::TERMINAL;
		$trtype     = self::TRTYPE_PREAUTH;
		$merch_name = self::MERCH_NAME;
		$merchant   = self::MERCHANT;
		$email      = self::EMAIL;
		$hmac       = self::getHMAC( $amount, $currency, $order, $merch_name, $merchant, $terminal, $email, $trtype, $timestamp, $nonce, $backref );
		$p_sign     = self::getPSign( $hmac );

		$request = [
			'AMOUNT'     => $amount, 'CURRENCY' => $currency, 'ORDER' => $order,
			'DESC'       => $desc, 'TERMINAL' => $terminal, 'TRTYPE' => $trtype,
			'MERCH_NAME' => $merch_name, 'MERCHANT' => $merchant, 'EMAIL' => $email,
			'TIMESTAMP'  => $timestamp, 'NONCE' => $nonce, 'BACKREF' => $backref, 'P_SIGN' => $p_sign,
		];

		self::saveRequest( $request );
		self::showForm( $request );
	}

	public static function finish( $rrn, $backref = self::BACKREF )
	{
		$req        = self::getRequest( $rrn );
		$order      = $req[ 'order' ];
		$amount     = $req[ 'amount' ];
		$org_amount = $req[ 'org_amount' ];
		$currency   = $req[ 'currency' ];
		$rrn        = $req[ 'rrn' ];
		$int_ref    = $req[ 'int_ref' ];
		$trtype     = self::TRTYPE_FINISH;
		$terminal   = self::TERMINAL;
		$email      = self::EMAIL;
		$timestamp  = self::getTimestamp();
		$nonce      = self::getNonce();
		$hmac       = self::getHMAC( $order, $amount, $currency, $org_amount, $rrn, $int_ref, $trtype, $terminal, $backref, $email, $timestamp, $nonce );
		$p_sign     = self::getPSign( $hmac );

		$request = [
			'ORDER'  => $order, 'AMOUNT' => $amount, 'CURRENCY' => $currency, 'RRN' => $rrn, 'INT_REF' => $int_ref,
			'TRTYPE' => $trtype, 'TERMINAL' => $terminal, 'BACKREF' => $backref, 'EMAIL' => $email, 'TIMESTAMP' => $timestamp,
			'NONCE'  => $nonce, 'P_SIGN' => $p_sign, 'ORG_AMOUNT' => $org_amount,
		];

		self::saveRequest( $request );
		self::showForm( $request );
	}

	public static function cancel( $rrn, $backref = self::BACKREF )
	{
		$req = self::getRequest( $rrn );

		$order      = self::getTimestamp();
		$amount     = $req[ 'amount' ];
		$currency   = $req[ 'currency' ];
		$org_amount = $req[ 'org_amount' ];
		$rrn        = $req[ 'rrn' ];
		$int_ref    = $req[ 'int_ref' ];
		$trtype     = self::TRTYPE_CANCEL;
		$terminal   = self::TERMINAL;
		$email      = self::EMAIL;
		$timestamp  = self::getTimestamp();
		$nonce      = self::getNonce();
		$hmac       = self::getHMAC( $order, $amount, $currency, $org_amount, $rrn, $int_ref, $trtype, $terminal, $backref, $email, $timestamp, $nonce );
		$p_sign     = self::getPSign( $hmac );

		$request = [
			'ORDER'  => $order, 'AMOUNT' => $amount, 'CURRENCY' => $currency, 'ORG_AMOUNT' => $org_amount, 'RRN' => $rrn, 'INT_REF' => $int_ref,
			'TRTYPE' => $trtype, 'TERMINAL' => $terminal, 'BACKREF' => $backref, 'EMAIL' => $email, 'TIMESTAMP' => $timestamp,
			'NONCE'  => $nonce, 'P_SIGN' => $p_sign,
		];

		self::saveRequest( $request );
		self::showForm( $request );
	}

	public static function response( $response )
	{
		$response  = self::cleanResponseFields( $response );
		$response  = self::prepareResponseFields( $response );
		$hmac_vars = self::getFieldsByTRType( $response[ 'TRTYPE' ] );
		$hmac      = self::prepareResponseHMAC( $response, $hmac_vars );
		$p_sign    = self::getPSign( $hmac );
		if ( $p_sign === $response[ 'P_SIGN' ] )
			self::saveResponse( $response );
	}

	private static function getRequest( $rrn )
	{
		$sql = sprintf( "SELECT `order`,`amount`,`currency`,`org_amount`,`rrn`,`int_ref`,`trtype`,`terminal`,
		`backref`,`email`,`timestamp`,`nonce`,`p_sign`
		FROM payment_pays WHERE rrn=%s AND rc='00' AND rctext='Approved' AND trtype IN (0,1)", DB::conn()->quote( $rrn ) );
		return DB::conn()->query( $sql )->fetch();
	}

	private static function getTimestamp()
	{
		return gmdate( "YmdHis", time() );
	}

	private static function getNonce()
	{
		return md5( self::getTimestamp() );
	}

	private static function getHMAC()
	{
		$hmac = '';
		foreach ( func_get_args() as $arg )
			if ( strlen( $arg ) != 0 )
				$hmac .= strlen( $arg ) . $arg;
			else
				$hmac .= '-';
		return $hmac;
	}

	private static function getPSign( $hmac )
	{
		return strtoupper( hash_hmac( 'sha1', $hmac, pack( 'H*', self::KEY ) ) );
	}

	private static function cleanResponseFields( $response )
	{
		foreach ( $response as $key => $val )
			if ( !in_array( $key, self::$FIELD_NAMES ) )
				unset( $response[ $key ] );
		return $response;
	}

	private static function prepareResponseFields( array $response )
	{
		$_response = [ ];
		foreach ( $response as $key => $value )
		{
			if ( $key == 'BACKREF' ) // decode URL
				$_response[ $key ] = urldecode( $value );
			else
				$_response[ $key ] = $value;
		}
		return $_response;
	}

	private static function getFieldsByTRType( $trtype )
	{
		$hmac_vars = [ ];
		if ( $trtype == self::TRTYPE_PREAUTH || $trtype == self::TRTYPE_PAY )
		{
			$hmac_vars = [ 'AMOUNT', 'CURRENCY', 'ORDER', 'MERCH_NAME', 'MERCHANT', 'TERMINAL', 'EMAIL', 'TRTYPE', 'TIMESTAMP',
				'NONCE', 'BACKREF', 'RESULT', 'RC', 'RCTEXT', 'AUTHCODE', 'RRN', 'INT_REF' ];
		}
		elseif ( $trtype == self::TRTYPE_CANCEL || $trtype == self::TRTYPE_FINISH )
		{
			$hmac_vars = [ 'ORDER', 'AMOUNT', 'CURRENCY', 'ORG_AMOUNT', 'RRN', 'INT_REF', 'TRTYPE', 'TERMINAL', 'BACKREF', 'EMAIL',
				'TIMESTAMP', 'NONCE', 'RESULT', 'RC', 'RCTEXT' ];
		}
		return $hmac_vars;
	}

	private static function prepareResponseHMAC( array $response, array $hmac_vars )
	{
		$hmac_array = [ ];

		foreach ( $hmac_vars as $param )
			if ( strlen( $response[ $param ] ) != 0 )
				$hmac_array[ ] = strlen( $response[ $param ] ) . $response[ $param ];
			else
				$hmac_array[ ] = '-';

		$hmac = implode( $hmac_array );

		return $hmac;
	}

	private static function showForm( array $params )
	{
		$pay_form_sfx = uniqid();
		?>
		<html>
		<body>
		<form id="pay-form-<?= $pay_form_sfx; ?>" action="<?= self::URL; ?>" method="post">
			<?php foreach ( $params as $par_name => $par_value ): ?>
				<input type="HIDDEN" value="<?= $par_value; ?>" NAME="<?= strtoupper( $par_name ); ?>">
			<?php endforeach; ?>
			<input type="SUBMIT" style="display:none;" value="">
		</form>
		</body>
		</html>;
		<script type="text/javascript">window.onload=function(){document.forms["pay-form-<?= $pay_form_sfx; ?>"].submit()}</script>;
	<?php
	}

	private static function saveRequest( $request )
	{
		$sql = sprintf( "INSERT INTO payment_pays(%s, `is_response`, `oper_time`) VALUES(%s, 0, NOW())",
			implode( ',', array_map( function ( $key ) { return sprintf( "`%s`", strtolower( $key ) ); }, array_keys( $request ) ) ),
			implode( ',', array_map( function ( $val ) { return DB::conn()->quote( $val ); }, array_values( $request ) ) )
		);
		L::log( $sql, 'saveRequest sql' );
		DB::conn()->exec( $sql );
	}

	private static function saveResponse( $response )
	{
		$sql = sprintf( "INSERT INTO payment_pays(%s, `is_response`, `oper_time`) VALUES(%s, 1, NOW())",
			implode( ',', array_map( function ( $key ) { return sprintf( "`%s`", strtolower( $key ) ); }, array_keys( $response ) ) ),
			implode( ',', array_map( function ( $val ) { return DB::conn()->quote( $val ); }, $response ) )
		);
		L::log( $sql, 'saveResponse sql' );
		DB::conn()->exec( $sql );
	}

}
/* Response Example
 * Array
	(
		[AMOUNT] => 245
		[ORG_AMOUNT] =>
		[CURRENCY] => RUB
		[ORDER] => 20131219132140
		[DESC] => за бытовую технику
		[MERCH_NAME] => test
		[MERCHANT] => 790367686219999
		[TERMINAL] => 79036861
		[EMAIL] => almatov.us@gmail.com
		[TRTYPE] => 1
		[TIMESTAMP] => 20131219132209
		[NONCE] => 6fda9d362be833da727da5e9ea16b391
		[BACKREF] => http://raybt.ru/
		[RESULT] => 0
		[RC] => 00
		[RCTEXT] => Approved
		[AUTHCODE] => 473503
		[RRN] => 335317540496
		[INT_REF] => F3FB8E140CB936B8
		[P_SIGN] => E5F6E07980E24FE983FD1E075BE963A425581ED8
		[NAME] => TEST
		[CARD] => 4268XXXXXXXX3212
		[CHANNEL] => VISA
	)
 */
